
#include <BFont>
#include <BStyle>
#include <BOpenGL>
#include <BPainter>
#include "member_BWidget.h"
#include "member_BStyle.h"
#include "member_BDrag.h"
#include "IDragBoard.h"
#include "IGlobal.h"

using namespace BWE;

IDragBoard::IDragBoard()
{
	drag = 0;
	root = 0;
	widget = 0;
	dirty = false;
	resized = true;
	fbo = 0;
	tbo = 0;
}
IDragBoard::~IDragBoard()
{

}

void IDragBoard::clear()
{
	drag = 0;
	root = 0;
	widget = 0;
	dirty = false;
	block.clear();
	styles.reset();
}
void IDragBoard::reset(BWidget* hoveredWidget)
{
	if (widget != hoveredWidget)
	{
		widget = hoveredWidget;
		styles.reset();
		if (widget)
		{
			root = widget->root();
		}
		else
		{
			root = 0;
		}
		dirty = true;
	}
}
void IDragBoard::freshStyles()
{
	BWidget* parent = widget;
	while (parent)
	{
		if (const BStyle* realStyle = parent->realStyle())
		{
			if (const BStyle* dragStyle = realStyle->annex("drag"))
			{
				styles.append(dragStyle);
			}
		}
		parent = parent->parent();
	}
	if (const BStyle* dragStyle = app_style->annex("drag"))
	{
		styles.append(dragStyle);
	}
}
const BValue& IDragBoard::findValue(StyleValue name)
{
	for (int i = 0; i < styles.size(); i++)
	{
		const BStyle* style = styles[i];
		const BValue& value = style_member(style)->valueMap(name);
		if (value.valid())
			return value;
	}
	return app_style->value(name);
}
const BColor* IDragBoard::findColor(StyleColor name)
{
	for (int i = 0; i < styles.size(); i++)
	{
		const BStyle* style = styles[i];
		auto it = style_member(style)->colorMap.find(name);
		if (it.valid())
			return &(*it);
	}
	auto it = style_member(app_style)->colorMap.find(name);
	if (it.valid())
		return &(*it);
	return 0;
}

bool IDragBoard::update()
{
	if (drag && drag_member(drag)->dirty)
	{
		drag_member(drag)->dirty = false;
		dirty = true;
		return true;
	}
	return false;
}
void IDragBoard::fresh()
{
	if (!widget)
		return;

	if (styles.empty())
		freshStyles();

	int b = findValue(Value_Border);
	int mleft = findValue(Value_Margin_Left);
	int mright = findValue(Value_Margin_Right);
	int mtop = findValue(Value_Margin_Top);
	int mbottom = findValue(Value_Margin_Bottom);

	block.text.reset();
	for (int i = 0; i < styles.size(); i++)
	{
		if (styles[i]->translate(drag->text(), block.text))
			break;
	}
	if (block.text.empty())
	{
		block.text = drag->text();
	}

	int pageWidth = 0;
	const BValue& widthValue = findValue(Value_Width);
	if (widthValue.valid())
		pageWidth = widthValue;
	if (pageWidth > root->width())
		pageWidth = root->width();

	const BValue& fontValue = findValue(Value_Font);
	block.align = findValue(Value_Align);
	if (fontValue.valid())
		block.font = (BFont*)fontValue;
	else
		block.font = widget->realFont();
	block.dirty = true;
	block.reset();
	block.adjust(pageWidth);
	if (block.width() < pageWidth)
		block.setWidth(pageWidth);

	alignment.ia_align = Align_Center;
	const BValue& alignValue = findValue(Value_Align);
	if (alignValue.valid())
		alignment.ia_align = alignValue;

	BImage* image = drag->image();
	if (image)
		alignment.ia_iconSize = image->size();
	else
		alignment.ia_iconSize.reset();
	alignment.ia_textSize = block.size();

	BRect rect = alignment.adjust(BSize(pageWidth, 0));
	alignment.adjust(BRect(b + mleft, b + mtop, rect.width(), rect.height()));
	block = alignment.ia_textRect;

	area.reset();
	area.expand(alignment.ia_iconRect);
	area.expand(alignment.ia_textRect);
	area.right() += b + mright;
	area.bottom() += b + mbottom;

	resized = true;

	if (b > 0)
	{
		int w = area.width();
		int h = area.height();
		rectLeft.set(0, b, b, h - b - b);
		rectTop.set(0, 0, w, b);
		rectRight.set(w - b, b, b, h - b - b);
		rectBottom.set(0, h - b, w, b);
	}

	BSize radiusLeftTop = findValue(Value_Radius_Left_Top);
	BSize radiusRightTop = findValue(Value_Radius_Right_Top);
	BSize radiusLeftBottom = findValue(Value_Radius_Left_Bottom);
	BSize radiusRightBottom = findValue(Value_Radius_Right_Bottom);
	if (radiusLeftTop.empty() && radiusRightTop.empty() && radiusLeftBottom.empty() && radiusRightBottom.empty())
	{
		round = 0;
	}
	else
	{
		if (round.empty())
			round = new IRound();
		round->radiusLeftTop = radiusLeftTop;
		round->radiusRightTop = radiusRightTop;
		round->radiusLeftBottom = radiusLeftBottom;
		round->radiusRightBottom = radiusRightBottom;
	}

	if (round)
	{
		int width = area.width();
		int height = area.height();
		round->frameSize.set((float)width, (float)height);
		round->centerLeftTop.set(round->radiusLeftTop.width(), round->radiusLeftTop.height());
		round->centerRightTop.set(width - round->radiusRightTop.width(), round->radiusRightTop.height());
		round->centerLeftBottom.set(round->radiusLeftBottom.width(), height - round->radiusLeftBottom.height());
		round->centerRightBottom.set(width - round->radiusRightBottom.width(), height - round->radiusRightBottom.height());

		BPointfArray outerPoints;
		BPointfArray innerPoints;

		BRectf outerRect(0, 0, width - 1, height - 1);
		round->createPoints(outerRect, outerPoints);

		BRectf innerRect = outerRect;
		innerRect.left() += b;
		innerRect.right() -= b;
		innerRect.top() += b;
		innerRect.bottom() -= b;

		round->createPoints(innerRect, innerPoints);
		round->createTriangles(outerPoints, innerPoints, round->borderPoints);

		round->backgroundPoints = innerPoints;
		round->backgroundPoints.prepend(block.center());

		outerPoints.reset();
		round->createLeftTopPoints(innerRect, outerPoints);
		round->createRightTopPoints(innerRect, outerPoints);

		outerPoints = innerPoints;
		round->createPoints(block, innerPoints);
		round->createTriangles(outerPoints, innerPoints, round->marginPoints);

		round->centerPoints = innerPoints;
		round->centerPoints.prepend(block.center());
	}
}
void IDragBoard::paint()
{
	BPainter painter(area.size());
	const BColor* background = findColor(Color_Background);
	if (background)
	{
		painter.setColor(*background);
		if (round)
		{
			glEnable(GL_MULTISAMPLE);
			glBegin(GL_TRIANGLE_FAN);
			for (int i = 0; i < round->backgroundPoints.size(); i++)
			{
				const BPointf& p = round->backgroundPoints[i];
				glVertex2f(p.x(), p.y());
			}
			glEnd();
			glDisable(GL_MULTISAMPLE);
		}
		else
		{
			painter.fillRect(0, area.size());
		}
	}
	const BColor* borderColor = findColor(Color_Border);
	if (borderColor)
	{
		painter.setColor(*borderColor);
		if (round)
		{
			glEnable(GL_MULTISAMPLE);
			glBegin(GL_TRIANGLES);
			for (int i = 0; i < round->borderPoints.size(); i++)
			{
				const BPointf& p = round->borderPoints[i];
				glVertex2f(p.x(), p.y());
			}
			glEnd();
			glDisable(GL_MULTISAMPLE);
		}
		else
		{
			painter.fillRect(rectLeft);
			painter.fillRect(rectTop);
			painter.fillRect(rectRight);
			painter.fillRect(rectBottom);
		}
	}
	if (BImage* image = drag->image())
	{
		painter.drawImage(alignment.ia_iconRect, image);
	}
	const BColor* textColor = findColor(Color_Text);
	if (textColor)
	{
		painter.setColor(*textColor);
		block.paint(painter);
	}
}
void IDragBoard::frame(GLuint msfbo, GLenum mode)
{
	int width = area.width();
	int height = area.height();
	if (fbo == 0 || tbo == 0)
		resized = true;
	if (fbo_size != area.size())
		resized = true;
	if (resized)
	{
		GLint backup_frame_buffer;
		glGetIntegerv(GL_FRAMEBUFFER_BINDING, &backup_frame_buffer);
		if (!fbo)
		{
			glGenFramebuffers(1, &fbo);
		}
		if (fbo)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, fbo);
		}
		if (!tbo)
		{
			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			glGenTextures(1, &tbo);
		}
		if (tbo)
		{
			glBindTexture(GL_TEXTURE_2D, tbo);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
			glBindTexture(GL_TEXTURE_2D, 0);
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tbo, 0);
		}
		glBindFramebuffer(GL_FRAMEBUFFER, backup_frame_buffer);
		fbo_size = area.size();
		resized = false;
	}
	if (fbo)
	{
		GLint backup_frame_buffer;
		glGetIntegerv(GL_FRAMEBUFFER_BINDING, &backup_frame_buffer);

		glBindFramebuffer(GL_FRAMEBUFFER, fbo);
		glDrawBuffer(GL_COLOR_ATTACHMENT0);
		glReadBuffer(GL_COLOR_ATTACHMENT0);

		if (msfbo > 0)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, msfbo);
			glDrawBuffer(mode);
			glReadBuffer(mode);
		}

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		glDisable(GL_LIGHTING);
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_CULL_FACE);
		glDisable(GL_MULTISAMPLE);

		glEnable(GL_BLEND);
		glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
		glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
		glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);

		glViewport(0, 0, width, height);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, width, height, 0, -1, 1);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		paint();

		if (msfbo > 0)
		{
			glBindFramebuffer(GL_READ_FRAMEBUFFER, msfbo);
			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
			glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
		}

		glBindFramebuffer(GL_FRAMEBUFFER, backup_frame_buffer);
	}
}

